package com.wang.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.client.utils.StringUtils;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wang.component.PayFactory;
import com.wang.config.RabbitMQConfig;
import com.wang.constant.CacheKey;
import com.wang.constant.TimeConstant;
import com.wang.enums.*;
import com.wang.exception.BizException;
import com.wang.feign.CouponFeignService;
import com.wang.feign.ProductFeignService;
import com.wang.feign.UserFeignService;
import com.wang.interceptor.LoginInterceptor;
import com.wang.mapper.ProductOrderItemMapper;
import com.wang.mapper.ProductOrderMapper;
import com.wang.model.LoginUser;
import com.wang.model.OrderMessage;
import com.wang.model.ProductOrderDO;
import com.wang.model.ProductOrderItemDO;
import com.wang.request.*;
import com.wang.service.ProductOrderService;
import com.wang.util.CommonUtil;
import com.wang.util.JsonData;
import com.wang.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {
    @Autowired
    private ProductOrderMapper productOrderMapper;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductOrderItemMapper productOrderItemMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 防重提交
     * * 用户微服务-确认收货地址
     * * 商品微服务-获取最新购物项和价格
     * * 订单验价
     * * 优惠券微服务-获取优惠券
     * * 验证价格
     * * 锁定优惠券
     * * 锁定商品库存
     * * 创建订单对象
     * * 创建子订单对象
     * * 发送延迟消息-用于自动关单
     * * 创建支付信息-对接三方支付
     *
     * @param confirmOrderRequest
     * @return
     */
    @Override
    @Transactional
    public JsonData confirmOrder(ConfirmOrderRequest confirmOrderRequest) throws AlipayApiException {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        //订单防重提交
        String orderToken = confirmOrderRequest.getToken();
        if(StringUtils.isBlank(orderToken)){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXIST);
        }
        //原子操作 校验令牌，删除令牌
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

        Long result = redisTemplate.execute(new DefaultRedisScript<>(script,Long.class), Arrays.asList(String.format(CacheKey.ORDER_TOKEN_KEY,loginUser.getId())),orderToken);
        if(result == 0L){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
        }


        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);

        //获取收货地址详情信息
        ProductOrderAddressVO addressVO = this.getUserAddress(confirmOrderRequest.getAddressId());
        log.info("收货地址信息：{}", addressVO);

        //获取用户加入购物车的商品详情信息
        List<Long> productIdList = confirmOrderRequest.getProductIdList();

        JsonData jsonData = productFeignService.confirmOrderCarItem(productIdList);
        List<OrderItemVO> orderDOList = jsonData.getData(new TypeReference<>() {});
        log.info("商品信息：{}", orderDOList);
        if(orderDOList == null){
            //购物车商品不存在
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }

        //验证价格，减去商品优惠券
        this.checkPrice(orderDOList,confirmOrderRequest);

        //锁定优惠券
        this.lockCouponRecord(confirmOrderRequest,orderOutTradeNo);

        //锁定商品库存
        this.lockProductStock(orderDOList,orderOutTradeNo);

        //创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(confirmOrderRequest,loginUser,orderOutTradeNo,addressVO);

        //创建订单项
        this.saveProductOrderItems(orderOutTradeNo,productOrderDO.getId(),orderDOList);

        //定时关单
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getOrderCloseDelayQueue(),orderMessage);

        //创建支付
        PayInfoVO payInfoVO = new PayInfoVO(orderOutTradeNo,
                confirmOrderRequest.getTotalAmount(),
                confirmOrderRequest.getPayType(),
                confirmOrderRequest.getClientType(),
                orderDOList.get(0).getProductTitle(),"",
                TimeConstant.ORDER_PAY_TIMEOUT_MILLS);
        String payResult = payFactory.pay(payInfoVO);
        if(StringUtils.isNotBlank(payResult)){
            log.info("创建支付订单成功:payInfoVO={},payResult={}",payInfoVO,payResult);
            return JsonData.buildSuccess(payResult);
        }else {
            log.error("创建支付订单失败:payInfoVO={},payResult={}",payInfoVO,payResult);
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
        }

    }

    /**
     * 创建订单项
     * @param orderOutTradeNo
     * @param orderId
     * @param orderItemList
     */
    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemList) {
        List<ProductOrderItemDO> list = orderItemList.stream().map(
                obj->{
                    ProductOrderItemDO itemDO = new ProductOrderItemDO();
                    itemDO.setBuyNum(obj.getBuyNum());
                    itemDO.setProductId(obj.getProductId());
                    itemDO.setProductImg(obj.getProductImg());
                    itemDO.setProductName(obj.getProductTitle());

                    itemDO.setOutTradeNo(orderOutTradeNo);
                    itemDO.setCreateTime(new Date());

                    //单价
                    itemDO.setAmount(obj.getAmount());
                    //总价
                    itemDO.setTotalAmount(obj.getTotalAmount());
                    itemDO.setProductOrderId(orderId);
                    return itemDO;
                }
        ).collect(Collectors.toList());


        productOrderItemMapper.insertBatch(list);

    }

    /**
     * 创建订单
     * @param confirmOrderRequest
     * @param orderOutTradeNo
     * @param addressVO
     * @return
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest confirmOrderRequest,LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {
        ProductOrderDO productOrderDO  = new ProductOrderDO();
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setNickname(loginUser.getName());

        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);

        //实际支付的价格
        productOrderDO.setPayAmount(confirmOrderRequest.getRealPayAmount());

        //总价，未使用优惠券的价格
        productOrderDO.setTotalAmount(confirmOrderRequest.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(confirmOrderRequest.getPayType()).name());

        productOrderDO.setReceiverAddress(JSON.toJSONString(addressVO));

        productOrderMapper.insert(productOrderDO);

        return productOrderDO;


    }

    /**
     * 锁定商品库存
     * @param orderDOList
     * @param orderOutTradeNo
     */
    private void lockProductStock(List<OrderItemVO> orderDOList, String orderOutTradeNo) {
        LockProductRequest lockProductRequest = new LockProductRequest();
        lockProductRequest.setOrderOutTradeNo(orderOutTradeNo);
        List<OrderItemRequest> itemRequestList = orderDOList.stream().map(obj -> {
            OrderItemRequest orderItemRequest = new OrderItemRequest();
            orderItemRequest.setProductId(obj.getProductId());
            orderItemRequest.setBuyNum(obj.getBuyNum());
            return orderItemRequest;
        }).collect(Collectors.toList());
        lockProductRequest.setOrderItemList(itemRequestList);


        JsonData jsonData = productFeignService.lockProduct(lockProductRequest);

        if(jsonData.getCode() != 0){
            log.error("锁定商品库存失败:{}",lockProductRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
        }
    }

    /**
     * 锁定优惠券
     * @param confirmOrderRequest
     * @param orderOutTradeNo
     */
    private void lockCouponRecord(ConfirmOrderRequest confirmOrderRequest, String orderOutTradeNo) {
        List<Long> lockCouponRecordIds = new ArrayList<>();
        if(confirmOrderRequest.getCouponRecordId() > 0){
            lockCouponRecordIds.add(confirmOrderRequest.getCouponRecordId());//目前项目只可以使用一个优惠券，如果后面需要修改成多个优惠券，这里进行多个添加即可

            LockCouponRecordsRequest  lockCouponRecordsRequest = new LockCouponRecordsRequest();
            lockCouponRecordsRequest.setOrderOutTradeNo(orderOutTradeNo);
            lockCouponRecordsRequest.setLockCouponRecordIds(lockCouponRecordIds);
            JsonData jsonData = couponFeignService.rockRecord(lockCouponRecordsRequest);

            if(jsonData.getCode() != 0){
                log.error("锁定优惠券失败:{}",lockCouponRecordsRequest);
                throw new BizException(BizCodeEnum.COUPON_RECORD_LOCK_FAIL);
            }
        }

    }

    /**
     * 验证价格
     * 1）统计全部商品的价格
     * 2）获取优惠券（判断是否满足优惠券的条件），总价再减去优惠券的价格就是最终的价格
     * @param orderDOList
     * @param confirmOrderRequest
     */
    private void checkPrice(List<OrderItemVO> orderDOList, ConfirmOrderRequest confirmOrderRequest) {
        BigDecimal realPayAmount = new BigDecimal("0");
        if(orderDOList != null){
            for(OrderItemVO orderItemVO : orderDOList){
                realPayAmount = realPayAmount.add(orderItemVO.getAmount());
            }
        }

        //获取优惠券，判断是否可以使用
        CouponRecordVO couponRecordVO = getCartCouponRecord(confirmOrderRequest.getCouponRecordId());

        //计算购物车价格，是否满足优惠券满减条件
        if(realPayAmount.compareTo(couponRecordVO.getConditionPrice())<0){
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
        }
        if(couponRecordVO.getPrice().compareTo(realPayAmount)>0){//判断优惠券减去的价格是否大于商品价格（虽然通常情况下不会出现）
            realPayAmount = BigDecimal.ZERO;
        }else{
            realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
        }

        if(realPayAmount.compareTo(confirmOrderRequest.getRealPayAmount()) != 0){
            log.error("订单检价失败:{}",confirmOrderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }

    /**
     *  获取优惠券
     * @param couponRecordId
     * @return
     */
    private CouponRecordVO getCartCouponRecord(Long couponRecordId) {
        if(couponRecordId == null || couponRecordId < 0){
           return null;
        }
        JsonData jsonData = couponFeignService.findUserCouponRecordById(couponRecordId);
        if(jsonData.getCode()==0){
           CouponRecordVO couponRecordVO = jsonData.getData(new TypeReference<>(){});

           if(!couponAvailable(couponRecordVO)){
               log.error("优惠券使用失败");
               throw new BizException(BizCodeEnum.COUPON_UNAVAILABLE);
           }
           return couponRecordVO;
        }

        return null;
    }

    /**
     * 判断优惠券是否可用
     * @param couponRecordVO
     * @return
     */
    private boolean couponAvailable(CouponRecordVO couponRecordVO) {
        if(couponRecordVO.getUseState().equalsIgnoreCase(CouponUserStateEnum.NEW.name())){
            long currentTimesTamp = CommonUtil.getCurrentTimesTamp();
            long end = couponRecordVO.getEndTime().getTime();
            long start = couponRecordVO.getStartTime().getTime();
            if(currentTimesTamp >= start && currentTimesTamp <= end){
                return true;
            }
        }
        return false;
    }


    /**
     * 获取收货地址详情
     *
     * @param addressId
     * @return
     */
    private ProductOrderAddressVO getUserAddress(long addressId) {
        JsonData jsonData = userFeignService.detail(addressId);

        if (jsonData.getCode() != 0) {
            log.error("获取收货地址失败，msg:{}", jsonData);
            throw new BizException(BizCodeEnum.ADDRESS_NO_EXITS);
        }

        ProductOrderAddressVO addressVO = jsonData.getData(new TypeReference<>() {
        });

        return addressVO;
    }

    /**
     * 查询订单支付状态
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public String queryProductOrderState(String outTradeNo) {
        ProductOrderDO outTradeNo1 = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", outTradeNo));

        if (outTradeNo1.getState() == null) {
            return "";
        } else {
            return outTradeNo1.getState();
        }
    }

    /**
     * 定时关单
     * @param orderMessage
     * @return
     */
    @Override
    public boolean closeOrder(OrderMessage orderMessage) {
        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no", orderMessage.getOutTradeNo()));

        if(productOrderDO == null){
            //订单不存在
            log.warn("订单不存在：{}",orderMessage);
            return true;
        }
        if(productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())){
            //已经支付   直接确认消息，订单已经支付
            log.info("订单已经支付:{}",orderMessage);
            return true;
        }
        //向第三方支付查询订单是否真的未支付
        PayInfoVO payInfoVO = new PayInfoVO();
        payInfoVO.setPayType(productOrderDO.getPayType());
        payInfoVO.setOrderTradeNo(orderMessage.getOutTradeNo());
        String payResult = payFactory.queryOrderSuccess(payInfoVO);

        //结果为空，则未支付成功，本地取消订单
        if(StringUtils.isBlank(payResult)){
            productOrderMapper.updateOrderPayState(productOrderDO.getOutTradeNo(),ProductOrderStateEnum.CANCEL.name(),ProductOrderStateEnum.NEW.name());
            log.info("结果为空，则未支付成功，本地取消订单:{}",orderMessage);
            return true;
        }else {
            //支付成功，主动的把订单状态改成UI就支付，造成该原因的情况可能是支付通道回调有问题
            log.warn("支付成功，主动的把订单状态改成UI就支付，造成该原因的情况可能是支付通道回调有问题:{}",orderMessage);
            productOrderMapper.updateOrderPayState(productOrderDO.getOutTradeNo(),ProductOrderStateEnum.PAY.name(),ProductOrderStateEnum.NEW.name());
            return true;
        }

    }

    /**
     * 支付通知结果更新订单状态
     * @param payType
     * @param paramsMap
     * @return
     */
    @Override
    public JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {
        if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType.name())){
            //支付宝支付
            //获取商户订单号
            String outTradeNo = paramsMap.get("out_trade_no");
            //交易的状态
            String tradeStatus = paramsMap.get("trade_status");

            if("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISHED".equalsIgnoreCase(tradeStatus)){
                //更新订单状态
                productOrderMapper.updateOrderPayState(outTradeNo,ProductOrderStateEnum.PAY.name(),ProductOrderStateEnum.NEW.name());
                return JsonData.buildSuccess();
            }

        } else if(payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())){
            //微信支付  TODO
        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    /**
     * 分页查询订单列表
     * @param page
     * @param size
     * @param state
     * @return
     */
    @Override
    public Map<String, Object> page(Integer page, Integer size, String state) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        Page<ProductOrderDO> pageInfo = new Page<>(page,size);

        IPage<ProductOrderDO> orderDOPage = null;

        if(StringUtils.isBlank(state)){
            orderDOPage = productOrderMapper.selectPage(pageInfo,new QueryWrapper<ProductOrderDO>().eq("user_id",loginUser.getId()));
        }else {
            orderDOPage = productOrderMapper.selectPage(pageInfo,new QueryWrapper<ProductOrderDO>().eq("user_id",loginUser.getId()).eq("state",state));
        }

        //获取订单列表
        List<ProductOrderDO> productOrderDOList =  orderDOPage.getRecords();

        List<ProductOrderVO> productOrderVOList =  productOrderDOList.stream().map(orderDO->{

            List<ProductOrderItemDO> itemDOList = productOrderItemMapper.selectList(new QueryWrapper<ProductOrderItemDO>().eq("product_order_id",orderDO.getId()));

            List<OrderItemVO> itemVOList =  itemDOList.stream().map(item->{
                OrderItemVO itemVO = new OrderItemVO();
                BeanUtils.copyProperties(item,itemVO);
                return itemVO;
            }).collect(Collectors.toList());

            ProductOrderVO productOrderVO = new ProductOrderVO();
            BeanUtils.copyProperties(orderDO,productOrderVO);
            productOrderVO.setOrderItemList(itemVOList);
            return productOrderVO;

        }).collect(Collectors.toList());

        Map<String,Object> pageMap = new HashMap<>(3);
        pageMap.put("total_record",orderDOPage.getTotal());
        pageMap.put("total_page",orderDOPage.getPages());
        pageMap.put("current_data",productOrderVOList);

        return pageMap;
    }


    /**
     *未支付订单再次支付
     * @param repayOrderRequest
     * @return
     * @throws AlipayApiException
     */
    @Override
    @Transactional
    public JsonData repay(RepayOrderRequest repayOrderRequest) throws AlipayApiException {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        ProductOrderDO productOrderDO = productOrderMapper.selectOne(new QueryWrapper<ProductOrderDO>().eq("out_trade_no",repayOrderRequest.getOutTradeNo()).eq("user_id",loginUser.getId()));

        log.info("订单状态:{}",productOrderDO);

        if(productOrderDO==null){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST);
        }

        //订单状态不对，不是NEW状态
        if(!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())){
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
        }else {
            //订单创建到现在的存活时间
            long orderLiveTime = CommonUtil.getCurrentTimesTamp() - productOrderDO.getCreateTime().getTime();
            //创建订单是临界点，所以再增加1分钟多几秒，假如29分，则也不能支付了
            orderLiveTime = orderLiveTime + 70*1000;


            //大于订单超时时间，则失效
            if(orderLiveTime>TimeConstant.ORDER_PAY_TIMEOUT_MILLS){
                return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
            }else {

                //记得更新DB订单支付参数 payType                 还可以增加订单支付信息日志  TODO
                productOrderDO.setPayType(repayOrderRequest.getPayType());
                productOrderMapper.update(productOrderDO,new QueryWrapper<ProductOrderDO>().eq("out_trade_no",repayOrderRequest.getOutTradeNo()));

                //总时间-存活的时间 = 剩下的有效时间
                long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;
                //创建支付
                PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(),
                        productOrderDO.getPayAmount(),repayOrderRequest.getPayType(),
                        repayOrderRequest.getClientType(), productOrderDO.getOutTradeNo(),"",timeout);

                log.info("payInfoVO={}",payInfoVO);
                String payResult = payFactory.pay(payInfoVO);
                if(StringUtils.isNotBlank(payResult)){
                    log.info("创建二次支付订单成功:payInfoVO={},payResult={}",payInfoVO,payResult);
                    return JsonData.buildSuccess(payResult);
                }else {
                    log.error("创建二次支付订单失败:payInfoVO={},payResult={}",payInfoVO,payResult);
                    return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
                }

            }


        }




    }

}
